<?php 

namespace YouXin\DeviceCommunication\WorkermanUtils;

use Illuminate\Console\Command;
use Workerman\Worker;
use Workerman\Lib\Timer;

abstract class OpenSocketUtil{
	protected abstract function externalAgreement();

	protected abstract function ip();

	protected abstract function insidePort();

	protected abstract function externalPort();

	public function start(){
		// 创建一个Worker监听20002端口，不使用任何应用层协议
        // $this->server = new Worker("tcp://192.168.10.10:20002");
        $this->server = new Worker($this->externalAgreement().'://'.$this->ip().':'.$this->externalPort());
        // 启动1个进程对外提供服务
        $this->server->count = 1;

        $this->server->onMessage = function ($connection,$data){
            $connection->lastMessageTime = time();

            return;
            
        };

        $this->server->onWorkerStart = function ($worker) {
            Timer::add(1, function() use($worker){
                $time_now = time();
                foreach($worker->connections as $connection) {
                    // 有可能该connection还没收到过消息，则lastMessageTime设置为当前时间
                    if (empty($connection->lastMessageTime)) {
                        $connection->lastMessageTime = $time_now;
                        continue;
                    }
                    // 上次通讯时间间隔大于心跳间隔，则认为客户端已经下线，关闭连接
                    if ($time_now - $connection->lastMessageTime > 1800) {
                        echo "Client ip {$connection->getRemoteIp()} timeout!!!\n";
                        $connection->close();
                    }
                }
            });

            //开启一个内部用的发送消息用的text协议，用于发送内部内部消息到tcp协议，通知前端
            $inner_text_worker = new Worker('text://0.0.0.0:'.$this->insidePort());
            $inner_text_worker->onMessage = function ($connection, $buffer) {


                //给指定的频道发送消息
                $ret = $this->sendMessgeByExternal($buffer);

                //通过内部频道给调用内部频道发送消息的方法发送错误消息
                $connection->send($ret ? 'success' : 'fail');
            };
            //开启内部频道监听
            $inner_text_worker->listen();
        };

        $this->server->onClose = function ($connection)
        {
            echo "connection closed from ip {$connection->getRemoteIp()}\n";
            if (isset($connection->dispatchId)) {
                unset($this->server->coordinateDispatchId[$connection->dispatchId]);
                echo "connection closed from ip {$connection->getRemoteIp()}\n";
            }
        };

        $this->server->onConnect = function ($connection)
        {
            echo "Socket连接成功\n";
        };
	}

	public function stop(){
		$this->server = new Worker($this->externalAgreement().'://'.$this->ip().':'.$this->externalPort());

        $this->server->reloadable = false;

        $this->server->onWorkerStop = function($worker)
        {
            echo "Socket服务停止\n";
        };
	}

	public function restart(){
		$this->server = new Worker($this->externalAgreement().'://'.$this->ip().':'.$this->externalPort());
        // 设置此实例收到reload信号后是否reload重启
        $this->server->reloadable = true;
        $this->server->onWorkerStart = function($worker)
        {
            echo "Socket服务重启...\n";
        };
	}

	public function reload(){
    	$this->server = new Worker($this->externalAgreement().'://'.$this->ip().':'.$this->externalPort());
        // 设置此实例收到reload信号后是否reload重启
        $this->server->reloadable = false;
        $this->server->onWorkerStart = function($worker)
        {
            echo "Socket服务重载...\n";
        };
    }


    protected function sendMessageByDispatchId($dispatchId, $message){
        if (isset($this->server->coordinateDispatchId[$dispatchId])) {
            $connection = $this->server->coordinateDispatchId[$dispatchId];
            $connection->send($message);
            return true;
        }
        return false;
    }

    protected function sendMessageByUid($uid, $message)
    {
        $worker = $this->server;
        if(isset($worker->uidConnections[$uid]))
        {
            $connection = $worker->uidConnections[$uid];
            $connection->send($message);
            return true;
        }
        return false;
    }

    protected function sendMessgeByExternal($message){
        $worker = $this->server;

        foreach($worker->connections as $connection)
        {
            $connection->send($message);
        }

        return true;
    }
}